//
//  World.m
//  CompGraf2
//
//  Created by Juan Niosi on 01/11/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "World.h"

@implementation World
@synthesize mainCharacter,terrain,entities,camera,enemies,lights,path;
static World *s_world;

- (id)init
{
    if (s_world != NULL)
        return s_world;
    
    self = [super init];
    if (self) {
        entities = [[NSMutableArray alloc] init];
        enemies = [[NSMutableArray alloc] init];
        lights = [[NSMutableArray alloc] init];
        path = NULL;
    }
    s_world = self;
    return self;
}

- (void)addEntity:(Entity *)ent
{
    [entities addObject:ent];
}

- (void)addEnemy:(Enemy *)en
{
    [enemies addObject:en];
}

- (void)addLight:(Light *)li
{
    [lights addObject:li];
}

- (void)removeEntity:(Entity *)ent
{
    [entities removeObject:ent];
}

- (void)removeEnemy:(Enemy *)en
{
    [enemies removeObject:en];
}

- (void)removeLight:(Light *)li
{
    [lights removeObject:li];
}

+(World *)getWorld
{
    return s_world;
}

-(void)worldClic:(CGPoint)point
{
    for (Enemy *enemy in enemies)
    {
        if ([self entityClic:point :enemy])
        {
            //TODO: clic on enemy
            NSLog(@"CLIC ON ENEMY");
            return;
        }
    }
    [self floorClic:point];
}

-(bool)entityClic:(CGPoint)point :(Entity *)ent
{
    Vector3 *posNear = [camera unproject:point :0];
    Vector3 *posFar = [camera unproject:point :1];
    
    Vector3 *dir = [Vector3 subtract:posFar :posNear];
    
    Bounds *eb = ent.bounds;
    GLfloat *pos = ent.position;
    
    Vector3 *normal = [Vector3 up];
    Vector3 *v1 = [[Vector3 alloc] init];
    Vector3 *v2 = [[Vector3 alloc] init];
    Vector3 *offset = [[Vector3 alloc] init];
    for (int i = 0; i < 5; i++)
    {
        
        switch (i)
        {
                //tapa superior
            case 0:
                v1.x = eb.xMax - eb.xMin;
                v1.y = 0;
                v1.z = 0;
                
                v2.x = 0;
                v2.y = 0;
                v2.z = eb.zMax - eb.zMin;
                
                offset.x = pos[0] + eb.xMin;
                offset.y = pos[1] + eb.yMax;
                offset.z = pos[2] + eb.zMin;
                break;
            case 1:
                v1.x = eb.xMax - eb.xMin;
                v1.y = 0;
                v1.z = 0;
                
                v2.x = 0;
                v2.y = eb.yMax - eb.yMin;
                v2.z = 0;
                
                offset.x = pos[0] + eb.xMin;
                offset.y = pos[1] + eb.yMin;
                offset.z = pos[2] + eb.zMin;
                break;
            case 2:
                v1.x = 0;
                v1.y = 0;
                v1.z = eb.zMax - eb.zMin;
                
                v2.x = 0;
                v2.y = eb.yMax - eb.yMin;
                v2.z = 0;
                
                offset.x = pos[0] + eb.xMin;
                offset.y = pos[1] + eb.yMin;
                offset.z = pos[2] + eb.zMin;
                break;
            case 3:
                v1.x = 0;
                v1.y = 0;
                v1.z = eb.zMax - eb.zMin;
                
                v2.x = 0;
                v2.y = eb.yMax - eb.yMin;
                v2.z = 0;
                
                offset.x = pos[0] + eb.xMax;
                offset.y = pos[1] + eb.yMin;
                offset.z = pos[2] + eb.zMin;
                break;
            case 4:
                v1.x = eb.xMax - eb.xMin;
                v1.y = 0;
                v1.z = 0;
                
                v2.x = 0;
                v2.y = eb.yMax - eb.yMin;
                v2.z = 0;
                
                offset.x = pos[0] + eb.xMax;
                offset.y = pos[1] + eb.yMin;
                offset.z = pos[2] + eb.zMax;
                break;
        }
        [offset scale:-1.0f];
        
        normal = [Vector3 cross:v1 :v2];
        float d = ([normal getX]*[offset getX] + [normal getY]*[offset getY] + [normal getZ]*[offset getZ]);
        
        float divisor = [Vector3 dot:dir :normal];
        
        if (divisor == 0)
        {
            NSLog(@"CANNOT DIVIDE BY ZERO");
            return false;
        }
        
        float lambda = (d-[Vector3 dot:posNear :normal]) / divisor;
        
        Vector3 *pos3D = [Vector3 add:posNear :[Vector3 scale:dir :lambda]];
        
        Vector3 *sub = [Vector3 subtract:pos3D :offset];
        bool inside = false;
        switch (i)
        {
            case 0:
                inside = [sub getX] >= 0 && [sub getX] <= [v1 getX] && [sub getZ] >= 0 && [sub getZ] <= [v2 getZ];
                break;
            case 1:
                inside = [sub getX] >= 0 && [sub getX] <= [v1 getX] && [sub getY] >= 0 && [sub getY] <= [v2 getY];
                break;
            case 2:
                inside = [sub getZ] >= 0 && [sub getZ] <= [v1 getZ] && [sub getY] >= 0 && [sub getY] <= [v2 getY];
                break;
            case 3:
                inside = [sub getZ] >= 0 && [sub getZ] <= [v1 getZ] && [sub getY] >= 0 && [sub getY] <= [v2 getY];
                break;
            case 4:
                inside = [sub getX] >= 0 && [sub getX] <= [v1 getX] && [sub getY] >= 0 && [sub getY] <= [v2 getY];
                break;
        }
        if (inside)
            return true;
    }
    return false;
}

-(void)floorClic:(CGPoint)point
{
    Vector3 *posNear = [camera unproject:point :0];
    Vector3 *posFar = [camera unproject:point :1];
    
    Vector3 *dir = [Vector3 subtract:posFar :posNear];
    
    Vector3 *up = [Vector3 up];
    
    float divisor = [Vector3 dot:dir :up];
    
    if (divisor == 0)
    {
        NSLog(@"CANNOT DIVIDE BY ZERO");
        return;
    }
    
    float lambda = -[Vector3 dot:posNear :up] / divisor;
    
    Vector3 *pos3D = [Vector3 add:posNear :[Vector3 scale:dir :lambda]];
    
    MapNode *node = [terrain vert2MapNode:pos3D];
    
    MapNode *nodePos = [mainCharacter getCurrentObjective];
    
    if (![terrain isOccupied:node.x :node.y])
    {
        path = [terrain getPath:nodePos :node];
        [mainCharacter setPath:path];
    }
}

- (void)dealloc
{
    [entities release];
    [enemies release];
    [lights release];
    [terrain release];
    [camera release];
    [mainCharacter release];
    [path release];
    
    [super dealloc];
}
@end
